#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using namespace log_ns;

enum {
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR
};

const int glistensockfd = -1;
const int gblcklog = 8;

using tcp_task_t = std::function<void()>;

class TcpServer {
private:
    void Service(int sockfd, InetAdrr& who) {
        while (true) {
            // 开始读和写
            char buff[1024];
            int n = read(sockfd, buff, sizeof(buff) - 1);
            if (n > 0) {
                buff[n] = 0;
                LOG(DEBUG, "%s send a message: %s\n", who.AddrString().c_str(), buff);
                std::string echo = "[" + who.AddrString() + "]> ";
                echo += buff;
                write(sockfd, echo.c_str(), echo.size());
            } else if (n == 0) {
                LOG(INFO, "client %s quit\n", who.AddrString().c_str());
                break;
            } else {
                LOG(FATAL, "read error\n");
                break;
            }
        }
        close(sockfd);
    }
    
public:
    TcpServer(uint16_t port)
        : _port(port),
          _listensocked(glistensockfd),
          _isrunning(false) 
    {}

    void Init() {
        // 先获取listensocked
        _listensocked = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocked < 0) {
            LOG(FATAL, "create listensockfd fail\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "get listensockfd success, listensockfd: %d\n", _listensocked);

        // 现在开始绑定
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        
        int bind_n = bind(_listensocked, (struct sockaddr*)&local, sizeof(local));
        if (bind_n < 0) {
            LOG(FATAL, "bind listensockfd fail\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        int n = listen(_listensocked, gblcklog);
        if (n < 0) {
            LOG(FATAL, "listen socket fail\n");
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen sucess\n");
    }

    // 创建一个内部类
    struct ThreadData {
        int _sockfd;
        InetAdrr _addr;
        TcpServer* _tcp_point;

        ThreadData(const int sockfd, const InetAdrr& addr, TcpServer* tcp)
            : _sockfd(sockfd),
              _addr(addr),
              _tcp_point(tcp)
        {}
    };

    static void* runServer(void* args) {
        // 将线程分离，防止线程
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->_tcp_point->Service(td->_sockfd, td->_addr);

        delete td;
        return nullptr;
    }

    void Start() {
        _isrunning = true;
        while (_isrunning) {
            // 现在开始收消息和发消息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensocked, (struct sockaddr*)&peer, &len);
            InetAdrr addr(peer);
            if (sockfd < 0) {
                LOG(ERROR, "%s get sockfd fail, the reason is %s\n", addr.AddrString().c_str(), strerror(errno));
                continue;
            }
            LOG(INFO, "get sockfd success, sockfd: %d\n", sockfd);
            // Service(sockfd, addr);
            // 串行运行长服务并不能满足多个用户访问服务器，需要使用并行运行才能满足
            // 1. 多进程 2、多线程 3、线程池
            // // 1. 多进程版本
            // pid_t id = fork();
            // if (id == 0) {
            //     // 让子进程关闭listensocked文件描述符
            //     close(_listensocked);
                
            //     // 创建孙子进程，直接让子进程退出，孙子进程会被bash接管
            //     // 这样长服务就会被孙子进程运行，孙子进程退出直接退出
            //     // 子进程也不会阻塞，可以让父进程继续等下去
            //     // 当然最好的方法是使用信号 signal(SIGCHLD, SIG_IGN) 操作
            //     if (fork() > 0) exit(0);
            //     Service(sockfd, addr);
            //     exit(0);
            // }
            // // 让父进程关闭sockfd文件描述符，防止文件描述符太多导致文件描述符泄露
            // close(sockfd);
            // pid_t n = waitpid(id, nullptr, 0);
            // if (n > 0) {
            //     LOG(INFO, "wait child process success\n");
            // }

            // // 2. 多线程
            // pthread_t tid;
            // ThreadData* data = new ThreadData(sockfd, addr, this);
            // pthread_create(&tid, nullptr, runServer, (void*)data);

            // 3. 线程池
            tcp_task_t task = std::bind(&TcpServer::Service, this, sockfd, addr);
            ThreadPool<tcp_task_t>::GetInstance()->Push(task);
        }


        _isrunning = false;
    }

    ~TcpServer() {
        if (_listensocked > 0) 
            close(_listensocked);
    }
private:
    uint16_t _port;
    int _listensocked;
    bool _isrunning;
};